package com.hoas.comparedb.lancher;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.setting.Setting;
import com.hoas.comparedb.core.*;
import com.hoas.comparedb.pojo.TableChange;
import com.hoas.comparedb.util.CompareReporter;
import com.hoas.comparedb.util.DBCompareCollector;
import com.hoas.comparedb.util.JdbcUrlSplitter;
import org.apache.commons.cli.*;

import java.io.File;
import java.io.FileWriter;
import java.io.Writer;
import java.net.URL;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CompareLauncher {

    public static String CMD_SO = "so";
    public static String CMD_SO_DB = "so_db";
    public static String CMD_CONFIG_FILE = "config_file";

    public static String CMD_SN = "sn";
    public static String CMD_SN_DB = "sn_db";


    public static String CMD_FORMAT = "format";

    public static String CMD_OUTPATH = "out_path";


    public static void main(String[] args) throws Exception {

        CommandLine cl = entry( args );

        System.out.println("=== compare start ===");
        ExecuteCommand executeCommand = buildExecuteCommand( cl );
        System.out.println("Execute command: " + executeCommand);
        try {
            execute( executeCommand );
            System.out.println("=== compare finished ===");

        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public static void execute(ExecuteCommand command) {
        Setting setting = loadSetting( command.getConfigFile()  );
        System.out.println("=== load setting ===");
        System.out.println(setting);
        System.out.println("=== crate reader ===");
        DBReader soReader = createSettingSourceReader( setting, command.getSo(), command.getSoDb());
        System.out.println("=== so reader ===");
        System.out.println(soReader);
        DBReader snReader = createSettingSourceReader( setting, command.getSn(), command.getSnDb());
        System.out.println("=== sn reader ===");
        System.out.println(snReader);
        try {

            System.out.println("=== execute command ===");
            List<TableChange> tableChanges = new DBCompare(soReader, snReader).compare();
            CompareReporter compareReporter = tableChanges.stream().collect(new DBCompareCollector());
            System.out.println("=== execute command success ===");
            System.out.println( compareReporter );
            if (CollectionUtil.contains( command.getFormat(), "sql") ) {
                String fmtfile = String.format("/compare_diff.%s.sql", soReader.getDb());
                File file = new File( command.getOutPath() + fmtfile );
                System.out.println("=== output sql format ===");
                System.out.println("fmtfile:" + file.getPath());
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }

                FileWriter fileWriter = new FileWriter( file  );

                try(DBWriter writer =  createSettingSourceWriter( snReader.getSource().getType(), fileWriter, new Properties() )) {
                    writer.write(soReader, snReader, tableChanges,compareReporter, "sql");
                }
            }

            if (CollectionUtil.contains( command.getFormat(), "html" )) {

                String fmtfile = String.format("/compare_diff.%s.html", soReader.getDb());
                File file = new File( command.getOutPath() + fmtfile );
                System.out.println("=== output html format ===");
                System.out.println("fmtfile:" + file.getPath());
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }

                FileWriter fileWriter = new FileWriter( file  );
                try(DBWriter writer =  createSettingSourceWriter( snReader.getSource().getType(), fileWriter, new Properties() )) {
                    writer.write(soReader, snReader, tableChanges,compareReporter, "html");
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static CommandLine entry(String[] args) {

        Options options = getOptions();

        DefaultParser parser = new DefaultParser();
        CommandLine cl = null;
        try {
            cl = parser.parse(options, args);

            if (cl.hasOption("h")) {
                HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp( "帮助：", options );
                System.exit(0);
            }

        } catch (ParseException e) {

            System.out.println(e.getMessage());

            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp( "帮助：", options );
            System.exit(0);
        }

        return cl;

    }

    private static Options getOptions() {
        Options options = new Options();
        {
            Option option = new Option(CMD_SO, true, "数据库配置文件分组");
            option.setRequired( false );
            options.addOption( option );
        }
        {
            Option option = new Option(CMD_SN, true, "数据库配置文件分组");
            option.setRequired( false );
            options.addOption( option );
        }

        options.addOption(CMD_CONFIG_FILE, true, "被参照的数据库配置文件");

        options.addOption(CMD_SO_DB, true, "数据库名 database[.table]");
        options.addOption(CMD_SN_DB, true, "被参照的数据库名");

        options.addOption(CMD_FORMAT, false, "输出格式:  sql/html");
        options.addOption("h", false, "帮助");
        return options;
    }

    private static Setting loadSetting(String configFile) {
        //自定义数据库Setting，更多实用请参阅Hutool-Setting章节
        String currentPath = System.getProperty("user.dir");
        try {

            String path = currentPath + configFile;
            File file = new File(path);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            return new Setting(file, CharsetUtil.CHARSET_UTF_8, true);

        } catch (Exception e) {

            String classPath = CompareLauncher.class.getClassLoader().getResource("/").getPath();
            String path = classPath + configFile;
            File file = new File(path);
            return new Setting(file, CharsetUtil.CHARSET_UTF_8, true);

        }

    }

    private static DBReader createSettingSourceReader(Setting setting, String group, String sdb) {
        System.out.println("=== create setting source reader ===");
        System.out.println("group:" + group);
        System.out.println("sdb:" + sdb);
        Setting groupSetting = setting.getSetting( group );

        String driver = groupSetting.getStr("driver");
        String url = groupSetting.getStr("url");
        String user = groupSetting.getStr("user");
        String pass = groupSetting.getStr("pass");
        String db = groupSetting.getStr("db", sdb);
        String type = groupSetting.getStr("type");

        CompareSource source = new CompareSource( driver, url);
        source.setUser( user);
        source.setPass( pass );
        source.setType( type );
        source.setDb( db );

        JdbcUrlSplitter splitter = new JdbcUrlSplitter(url);
        if (source.getType() == null || source.getType().length() == 0) {
            source.setType( splitter.getDriverName() );
        }

        if (source.getDb() == null || source.getDb().length() == 0) {
            source.setDb( splitter.getDatabase() );
        }

        return CompareServiceLoader
                .getBuilder( source.getType() )
                .createReader( source, sdb);

    }

    private static DBWriter createSettingSourceWriter(String type, Writer writer, Properties properties) {
        return CompareServiceLoader
                .getBuilder( type )
                .createWriter(writer, properties);

    }


    private static ExecuteCommand buildExecuteCommand(CommandLine cl) {

        String config_file = cl.getOptionValue(CMD_CONFIG_FILE, "/conf/source.setting");

        // 比较源
        String so = cl.getOptionValue(CMD_SO);
        String so_db = cl.getOptionValue(CMD_SO_DB);

        // 参照源
        String sn = cl.getOptionValue(CMD_SN);
        String sn_db = cl.getOptionValue(CMD_SN_DB);

        List<String> format = Stream.of(cl.getOptionValue(CMD_FORMAT, "html,sql").split(",")).
                collect(Collectors.toList());

        String outPath = cl.getOptionValue(CMD_OUTPATH, System.getProperty("user.dir") + "/out");

        ExecuteCommand executeCommand = new ExecuteCommand();
        executeCommand.setConfigFile( config_file );
        executeCommand.setSo( so );
        executeCommand.setSn( sn );
        executeCommand.setSoDb( so_db );
        executeCommand.setSnDb( sn_db );
        executeCommand.setFormat( format );
        executeCommand.setOutPath( outPath );

        return executeCommand;

    }

}
